package com.dongjiantong.openplatform.core;/**
 * Created by cao on 2017/12/13.
 */

import com.dongjiantong.openplatform.entity.OPClientInfo;
import com.dongjiantong.openplatform.entity.OPRequest;
import com.dongjiantong.openplatform.core.OPInvoker.MethodInfo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.kevinsawicki.http.HttpRequest;
import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.collect.Maps;
import com.google.common.eventbus.Subscribe;
import com.google.common.hash.Hashing;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;

/**
 * @author cao
 * @create 2017-12-13 下午8:24
 **/
public class OPEvenSubscriber implements EventListener {
    private static final Logger log = LoggerFactory.getLogger(OPEvenSubscriber.class);
    private final OPInvoker invoker;
    private final SecurityManager securityManager;
    private final List<PushPostHandle> pushPostHandles;
    private final OpenProperties properties;
    private static final ObjectMapper objectMapper = new ObjectMapper();

    public OPEvenSubscriber(OPInvoker invoker, SecurityManager securityManager, List<PushPostHandle> pushPostHandles, OpenProperties properties) {
        this.invoker = invoker;
        this.securityManager = securityManager;
        this.pushPostHandles = pushPostHandles;
        this.properties = properties;
    }

    @Subscribe
    public void onExecute(OPPushEvent event) throws NoSuchFieldException, UnsupportedEncodingException {
        String method = event.getMethod();
        if(!StringUtils.hasText(method)) {
            log.error("push event client {} missing", event.getClientInfo());
        } else {
            OPClientInfo clientInfo = event.getClientInfo();
            if(clientInfo != null && clientInfo.getClientId() != null) {
                if(!this.securityManager.hasPermission(clientInfo.getClientId(), method)) {
                    log.error("client(id={}) has no permission to call {}", clientInfo.getClientId(), method);
                } else {
                    OPHook hook = this.securityManager.getHook(clientInfo.getClientId(), method);
                    if(hook != null && StringUtils.hasText(hook.getUrl())) {
                        Object result = this.invoker.execute(method, hook.getHttpMethod().name(), event.getParams(), false);
                        OPRequest req;
                        if(!(result instanceof OPRequest)) {
                            req = OPRequest.ok(result);
                        } else {
                            req = (OPRequest)result;
                        }

                        MethodInfo methodInfo = this.invoker.getMethodInfo(method);
                        HttpRequest request;
                        String url;
                        TreeMap sortedParams;
                        String sign;
                        if(hook.getHttpMethod() == RequestMethod.GET) {
                            if(!(result instanceof Map)) {
                                return;
                            }

                            try {
                                Map<String, Object> params = (Map)req.getParams();
                                sortedParams = Maps.newTreeMap();
                                sortedParams.remove("sign");
                                Iterator var12 = params.entrySet().iterator();

                                while(var12.hasNext()) {
                                    Map.Entry<String, Object> entry = (Map.Entry)var12.next();
                                    if(entry.getValue() != null) {
                                        sortedParams.put(entry.getKey(), entry.getValue().toString());
                                    }
                                }

                                sign = this.generateSign(clientInfo.getAppSecret(), sortedParams);
                                sortedParams.put("sign", sign);
                                Iterator var21 = sortedParams.entrySet().iterator();

                                while(var21.hasNext()) {
                                    Map.Entry<String, String> entry = (Map.Entry)var21.next();
                                    sortedParams.put(entry.getKey(), URLEncoder.encode((String)entry.getValue(), "UTF-8"));
                                }

                                url = hook.getUrl() + "?" + Joiner.on('&').withKeyValueSeparator("=").join(params);
                                request = HttpRequest.get(url).connectTimeout(this.properties.getPushConnectTimeOut()).readTimeout(this.properties.getPushReadTimeout());
                            } catch (ClassCastException var16) {
                                log.error("fail to cast class{} to map", req.getClass());
                                return;
                            }
                        } else {
                            if(hook.getHttpMethod() != RequestMethod.POST) {
                                return;
                            }

                            try {
                                String data = objectMapper.writeValueAsString(req);
                                sortedParams = Maps.newTreeMap();
                                sortedParams.put("appKey", clientInfo.getAppKey());
                                sortedParams.put("pampasCall", method);
                                sortedParams.put("data", URLEncoder.encode(data, "UTF-8"));
                                sign = this.generateSign(clientInfo.getAppSecret(), sortedParams);
                                sortedParams.put("sign", sign);
                                data = (String)sortedParams.remove("data");
                                url = hook.getUrl();
                                request = HttpRequest.post(url).headers(sortedParams).send(data).connectTimeout(this.properties.getPushConnectTimeOut()).readTimeout(this.properties.getPushReadTimeout());
                            } catch (JsonProcessingException var15) {
                                log.error("fail to parse json with {}", req);
                                return;
                            }
                        }

                        int code = request.code();
                        String body = request.body();
                        if(code != 200) {
                            log.error("fail to send get request {} with params {}, code {}", new Object[]{url, req.getParams(), Integer.valueOf(code), body});
                        }

                        if(log.isDebugEnabled()) {
                            log.debug("push to {} with params:{} code:{} body:{}", new Object[]{url, req.getParams(), Integer.valueOf(code), body});
                        }

                        this.postHandle(methodInfo, req, code, body);
                    }
                }
            } else {
                log.error("push event (method:{}) client {} info incorrect", event.getMethod(), event.getClientInfo());
            }
        }
    }

    private void postHandle(MethodInfo methodInfo, OPRequest req, int code, String body) {
        if(this.pushPostHandles != null) {
            Iterator var5 = this.pushPostHandles.iterator();

            while(var5.hasNext()) {
                PushPostHandle handle = (PushPostHandle)var5.next();
                if(handle.getClass() == methodInfo.getPushPostHandleClass()) {
                    try {
                        handle.handle(req, code, body);
                    } catch (Exception var8) {
                        ;
                    }
                }
            }

        }
    }

    private String generateSign(String secret, Map<String, String> params) {
        String toVerify = Joiner.on('&').withKeyValueSeparator("=").join(params);
        return Hashing.md5().newHasher().putString(toVerify, Charsets.UTF_8).putString(secret, Charsets.UTF_8).hash().toString();
    }
}
